/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <future>
#include "imageknife_internal.h"
#include "file_cache.h"
#include "default_cache_key_generator.h"
#include "imageknife_internal.h"
#include "imageknife_napi.h"

namespace ImageKnifeC {

// static
ImageKnife &ImageKnife::GetInstance()
{
    static ImageKnifeInternal imageKnifeInternal;
    imageKnifeInternal.keyPtr_ = new ImageKnifeC::DefaultCacheKeyGenerator();
    return imageKnifeInternal;
}

ImageKnifeInternal& ImageKnifeInternal::GetInstance()
{
    return static_cast<ImageKnifeInternal&>(ImageKnife::GetInstance());
}

void ImageKnifeInternal::SetRootNode(std::string id, ArkUI_NodeContentHandle handle,
                                     std::shared_ptr<ImageKnifeNode> node)
{
    auto internalNode = std::static_pointer_cast<ImageKnifeNodeInternal>(node);
    contentHandleMap_[id] = contentHandle({.imageNode = internalNode, .rootNode = handle});
    // 添加Native组件到NodeContent上用于挂载显示。
    OH_ArkUI_NodeContent_AddNode(handle, internalNode->GetHandle());
}

void ImageKnifeInternal::DisposeRootNode(std::string id)
{
    // 从NodeContent上卸载组件并销毁Native组件。
    OH_ArkUI_NodeContent_RemoveNode(contentHandleMap_[id].rootNode, contentHandleMap_[id].imageNode->GetHandle());
    contentHandleMap_.erase(id);
}

std::shared_ptr<ImageKnifeNodeInternal> ImageKnifeInternal::GetImageNode(std::string id)
{
    if (contentHandleMap_.find(id) != contentHandleMap_.end()) {
        return contentHandleMap_[id].imageNode;
    }

    return nullptr;
}

void ImageKnifeInternal::Execute(std::shared_ptr<ImageKnifeRequest> request)
{
    this->dispatcher->Enqueue(std::static_pointer_cast<ImageKnifeRequestInternal>(request));
}

void ImageKnifeInternal::CancelRequest(std::shared_ptr<ImageKnifeRequest> request)
{
    this->dispatcher->CancelRequest(std::static_pointer_cast<ImageKnifeRequestInternal>(request));
}

std::shared_future<std::string> ImageKnifeInternal::InitFileCacheAsync(std::string cachePath,
    size_t size, size_t memory, std::string path)
{
    // 创建线程对象并分离线程
    std::thread initThread(&ImageKnifeInternal::InitFileCache, this, cachePath, size, memory, path);
    initThread.detach();  // 分离线程，主线程不需要等待
    return FileCache::GetInstance()->promise.get_future().share();
}

void ImageKnifeInternal::InitFileCache(std::string cachePath, size_t size, size_t memory, std::string path)
{
    if (path.empty()) {
        path = "ImageKnife";
    }

    // 初始化文件缓存
    FileCache::Init(size, memory);
    FileCache::GetInstance()->filePath = cachePath + "/" + path;

    FileCache::GetInstance()->InitFileCache();
}

void ImageKnifeInternal::SetCacheKeyGenerator(CacheKeyGenerator *keyPtr)
{
    if (keyPtr == nullptr) {
        return;
    }

    if (keyPtr_ != nullptr) {
        delete keyPtr_;
        keyPtr_ = nullptr;
    }

    keyPtr_ = keyPtr;
}

CacheKeyGenerator *ImageKnifeInternal::GetCacheKeyGenerator() const
{
    return keyPtr_;
}

void ImageKnifeInternal::SetMaxRequests(int concurrency)
{
    dispatcher->maxRequests = concurrency;
}

std::shared_ptr<ImageKnifeLoader> ImageKnifeInternal::GetDefaultImageKnifeLoader() const
{
    return imageLoader_;
}

void ImageKnifeInternal::SetDefaultImageKnifeLoader(std::shared_ptr<ImageKnifeLoader> imageLoader)
{
    imageLoader_ = imageLoader;
}

std::shared_ptr<ImageKnifeRequest> ImageKnifeInternal::Preload(std::shared_ptr<ImageKnifeOption> imageKnifeOption)
{
    auto request = std::make_shared<ImageKnifeRequestInternal>(imageKnifeOption);
    ImageKnifeInternal::GetInstance().Execute(request);
    return request;
}

int64_t ImageKnifeInternal::GetCacheLimitSize(CacheStrategy cacheStrategy)
{
    if (cacheStrategy == CacheStrategy::CACHE_FILE) {
        if (FileCache::GetInstance()->IsFileInitComplete()) {
            return FileCache::GetInstance()->GetAllowMaxDiskLimit();
        } else {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                         "ImageKnifeInternal::GetCacheLimitSize",
                         "the disk cache not init");
            return -1;
        }
    } else {
        return MemoryCache::GetInstance()->GetAllowMaxMemory();
    }
}

int64_t ImageKnifeInternal::GetCurrentCacheNum(CacheStrategy cacheStrategy)
{
    if (cacheStrategy == CacheStrategy::CACHE_FILE) {
        if (FileCache::GetInstance()->IsFileInitComplete()) {
            return FileCache::GetInstance()->Size();
        } else {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                         "ImageKnifeInternal::GetCurrentCacheNum",
                         "the disk cache not init");
            return -1;
        }
    } else {
        return MemoryCache::GetInstance()->Size();
    }
}

int64_t ImageKnifeInternal::GetCurrentCacheSize(CacheStrategy cacheStrategy)
{
    if (cacheStrategy == CacheStrategy::CACHE_FILE) {
        if (FileCache::GetInstance()->IsFileInitComplete()) {
            return FileCache::GetInstance()->GetCurrentMemory();
        } else {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                         "ImageKnifeInternal::GetCurrentCacheSize",
                         "the disk cache not init");
            return -1;
        }
    } else {
        return MemoryCache::GetInstance()->GetCurrentMemory();
    }
}

bool ImageKnifeInternal::RemoveFileCache(std::string &url)
{
    if (keyPtr_ == nullptr) {
        return false;
    }
    ImageSource  imageSource(url);
    std::string key = keyPtr_->GenerateFileKey(&imageSource);
    FileCache *fileCache = FileCache::GetInstance();
    if (fileCache == nullptr) {
        return false;
    } else {
        return fileCache->RemoveFileCache(key);
    }
}
bool ImageKnifeInternal::RemoveFileCache(std::shared_ptr<ImageKnifeOption> option)
{
    if (option == nullptr || keyPtr_ == nullptr) {
        return false;
    }
    std::string key = keyPtr_->GenerateFileKey(&option->loadSrc, option->signature);
    FileCache *fileCache = FileCache::GetInstance();
    if (fileCache == nullptr) {
        return false;
    } else {
        return fileCache->RemoveFileCache(key);
    }
}
bool ImageKnifeInternal::RemoveAllFileCache(void)
{
    FileCache *fileCache = FileCache::GetInstance();
    if (fileCache != nullptr) {
        return fileCache->RemoveAllFileCache();
    }
    return false;
}

std::shared_future<bool> ImageKnifeInternal::RemoveFileAsync(std::string &fileName)
{
    auto promise = std::make_shared<std::promise<bool>>();
    std::thread removeFileThread([this, &fileName, promise](){
        promise->set_value(RemoveFileCache(fileName));
    });
    removeFileThread.detach();
    return promise->get_future().share();
}

std::shared_future<bool> ImageKnifeInternal::RemoveFileAsync(std::shared_ptr<ImageKnifeOption> option)
{
    auto promise = std::make_shared<std::promise<bool>>();
    std::thread removeFileThread([this, option, promise](){
        promise->set_value(RemoveFileCache(option));
    });
    removeFileThread.detach();
    return promise->get_future().share();
}

std::shared_future<bool> ImageKnifeInternal::RemoveAllFileAsync(void)
{
    auto promise = std::make_shared<std::promise<bool>>();
    std::thread removeAllFileThread([this, promise](){
        promise->set_value(RemoveAllFileCache());
    });
    removeAllFileThread.detach();
    return promise->get_future().share();
}

void ImageKnifeInternal::RemoveAllMemoryCache()
{
    auto cachePtr = MemoryCache::GetInstance();
    if (cachePtr == nullptr) {
        return;
    }

    cachePtr->MemoryCache::GetInstance()->Clear();
}

bool ImageKnifeInternal::RemoveMemoryCache(std::string url)
{
    if (keyPtr_) {
        return false;
    }

    ImageKnifeOption imageKnifeOption;
    imageKnifeOption.loadSrc.SetString(url);
    CacheKeyGenerator::MemoryKeyParams memoryKeyParams;
    memoryKeyParams.imageSource = &imageKnifeOption.loadSrc;
    memoryKeyParams.type = ImageRequestType::MAIN_SRC;
    std::string key =
        keyPtr_->GenerateMemoryKey(memoryKeyParams);
    return MemoryCache::GetInstance()->Remove(key);
}

bool ImageKnifeInternal::RemoveMemoryCache(std::shared_ptr<ImageKnifeOption> imageKnifeOption)
{
    if (keyPtr_) {
        return false;
    }

    CacheKeyGenerator::MemoryKeyParams memoryKeyParams;
    memoryKeyParams.imageSource = &imageKnifeOption->loadSrc;
    memoryKeyParams.type = ImageRequestType::MAIN_SRC;
    std::string key = keyPtr_->GenerateMemoryKey(memoryKeyParams);
    return MemoryCache::GetInstance()->Remove(key);
}

void ImageKnifeInternal::InitImageKnifeArkTs(napi_env env, napi_value &exports)
{
    ImageKnifeNapi::Init(env, exports);
}

} // namespace ImageKnifeC